package cn.drh.charging.order.service.impl;


import cn.drh.charging.common.pojo.JsonResult;
import cn.drh.charging.common.pojo.param.GunStatusUpdateParam;
import cn.drh.charging.common.pojo.vo.StationInfoVO;
import cn.drh.charging.common.pojo.vo.UserInfoVO;
import cn.drh.charging.common.utils.JsonUtils;
import cn.drh.charging.order.config.RestTemplateConfiguration;
import cn.drh.charging.order.constant.Constant;
import cn.drh.charging.order.constant.MqttConstant;
import cn.drh.charging.order.dao.repository.OrderRepository;
import cn.drh.charging.order.feign.DeviceClient;
import cn.drh.charging.order.feign.UserClient;
import cn.drh.charging.order.job.DeviceCheckJob;
import cn.drh.charging.order.mqtt.MqttProducer;
import cn.drh.charging.order.pojo.dto.ChargingDto;
import cn.drh.charging.order.pojo.dto.StationInfoDto;
import cn.drh.charging.order.pojo.dto.UserInfoDto;
import cn.drh.charging.order.pojo.param.OrderAddParam;
import cn.drh.charging.order.pojo.po.OrderMQPO;
import cn.drh.charging.order.rabbitmq.RabbitMQOrderProducer;
import cn.drh.charging.order.service.OrderService;
import javassist.compiler.ast.Variable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Random;

/**
 * @ClassName OrderServiceImpl
 * @Author renhong~Deng
 * @Date 2025/5/26 15:19
 * @Version 1.0
 */

@Slf4j
@Service

public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Override
    public String createOrder(OrderAddParam orderAddParam) {
        //创建订单的流程 高内聚 低耦合 单一职责 解耦（你依赖我，我改变了你也跟着变，我们耦合在一起）
        //创建订单的流程
        //1.创建订单编号
        log.debug("开始生成订单号");
        String orderNo = getOrderId();
        log.debug("生成订单号：{}", orderNo);
        //2.获取运营商和场站信息
        log.debug("获取运营商和场站信息-入参-枪id:{}", orderAddParam.getGunId());
        StationInfoDto stationInfoDto = getStationInfoByGunId(orderAddParam.getGunId());
        log.debug("获取运营商和场站信息-入参-枪id:{},出参-{}", orderAddParam.getGunId(), stationInfoDto);
        //3.用户车辆信息
        log.debug("获取用户信息-入参-用户id:{}", orderAddParam.getUserId());
        UserInfoDto userInfoDto = getUserInfoByUserId(orderAddParam.getUserId());
        log.debug("获取用户信息-入参-用户id:{},出参-{}", orderAddParam.getUserId(), userInfoDto);
        //4.修改充电桩的状态 把充电桩状态 变为 已使用
//        Boolean success = updataGunStatus(orderAddParam.getGunId(), 2);
        log.debug("修改充电桩状态-入参-枪id:{}", orderAddParam.getGunId());
        Boolean success = updateGunStatusBusy(orderAddParam.getGunId());
        log.debug("修改充电桩状态-入参-枪id:{},出参-{}", orderAddParam.getGunId(), success);
        //5.检查用户余额todo
        //  5.1 要 更安全 提前检查，但是有可能，充电桩故障，根本充不了电，代码逻辑多跑了
        //  5.2 不要 什么时候检查呢? 应该充电桩正常，能充电的时候，每次充电桩同步充电进度
        //总结 用户余额检查 创建订单的时候可以检查，每次同步充电进度的时候必须检查
        //6.给充电桩发送开始指令
        //7.创建自检定时任务
        log.debug("创建自检定时任务-入参-订单号:{} 枪id：{}", orderNo, orderAddParam.getGunId());
        try {
            DeviceCheckJob deviceCheckJob = new DeviceCheckJob(orderNo, orderAddParam.getGunId());
        } catch (Exception e) {
            //可以抛出一个业务异常，全局异常处理器处理
            log.error("设备自检任务创建失败",e);
        }
        log.debug("创建自检定时任务成功-入参-订单号:{} 枪id：{}", orderNo, orderAddParam.getGunId());

        log.debug("给充电桩发送开始指令-入参-订单号:{},桩编号:{},枪编号:{}", orderNo, orderAddParam.getPileId(), orderAddParam.getGunId());
        startCharging(orderNo, orderAddParam.getPileId(), orderAddParam.getGunId());
        log.debug("成功给充电桩发送开始指令-入参-订单号:{},桩编号:{},枪编号:{}", orderNo, orderAddParam.getPileId(), orderAddParam.getGunId());

        log.debug("给mq发送开始指令-入参-订单号:{}", orderNo);
        sendOrderMessage(orderNo);
        return orderNo;
    }

    @Autowired
    RabbitMQOrderProducer rabbitMQOrderProducer;

    /**
     * 发送消息
     *
     * @param orderNo 订单号
     */
    private void sendOrderMessage(String orderNo) {
        OrderMQPO orderMQPO = new OrderMQPO();
        orderMQPO.setOrderNo(orderNo);
        rabbitMQOrderProducer.sendOrder(orderMQPO);
    }

    @Autowired
    private MqttProducer mqttProducer;

    /**
     * 给充电桩开始发送充电指令
     *
     * @param orderNo 订单号
     * @param pileId  桩编号
     * @param gunId   枪编号
     */
    private void startCharging(String orderNo, Integer pileId, Integer gunId) {
        log.debug("发送指令:订单号-{},桩id-{},枪id-{}",orderNo,pileId,gunId);
        String topic = MqttConstant.TOPIC_START_CHARGING_PREFIX + pileId;
        ChargingDto chargingDto = new ChargingDto();
        chargingDto.setOrderNo(orderNo);
        chargingDto.setPileId(pileId);
        chargingDto.setGunId(gunId);
        chargingDto.setMsg(Constant.START_CHARGING);
        String json = JsonUtils.toJson(chargingDto);
        log.debug("给设备发送消息，topic={}，message={}", topic, json);
        mqttProducer.send(topic, json);
    }

    /**
     * 把充电枪的状态改为正在使用
     *
     * @param gunId 枪编号
     * @return 是否成功
     */
    private Boolean updateGunStatusBusy(Integer gunId) {
        GunStatusUpdateParam param = new GunStatusUpdateParam();
        param.setGunId(gunId);
//        param.setStatus(2);代码中不要有魔数，应该用常量或者枚举
        param.setStatus(Constant.GUN_STATS_BUSY);
        JsonResult<Boolean> jsonResult = deviceClient.update(param);
        if (jsonResult != null) {
            return jsonResult.getData();
        }
        return false;
    }


    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private UserClient userClient;


    /**
     * 用户id获取用户车辆信息
     *
     * @param userId 用户id
     * @return 返回用户车辆信息
     */
    private UserInfoDto getUserInfoByUserId(Integer userId) {
        return getUserInfoByUserIdByFeign(userId);
    }


    /**
     * 通过feign调用用户服务获取用户车辆信息
     *
     * @param userId 用户id
     * @return 返回用户车辆信息
     */
    private UserInfoDto getUserInfoByUserIdByFeign(Integer userId) {
        JsonResult<UserInfoVO> userCarInfo = userClient.getUserCarInfo(userId);
        if (userCarInfo != null) {
            UserInfoVO data = userCarInfo.getData();
            if (data != null) {
                UserInfoDto userInfoDto = new UserInfoDto();
                userInfoDto.setCarId(data.getCarId());
                return userInfoDto;
            }
        }

        return null;
    }

    /**
     * 获取用户信息，绑定的车辆信息
     * 调用用户服务的一个接口 来获取用户绑定的车辆信息
     *
     * @param userId 用户id
     * @return 用户信息
     */
    private UserInfoDto getUserInfoByUserIdByRestTemplate(Integer userId) {
        //指定返回参数的类型
        ParameterizedTypeReference<JsonResult<UserInfoVO>> parameterizedTypeReference =
                new ParameterizedTypeReference<JsonResult<UserInfoVO>>() {
                };
        //随机获取一个url
        String url = chooseUrl();
        log.debug("通过随机算法获取用户服务url:{}", url);
        log.debug("通过RestTemplate调用用户服务，获取用户服务地址:{},入参为：{}", url, userId);

        ResponseEntity<JsonResult<UserInfoVO>> entity =
                restTemplate.exchange(url, HttpMethod.GET, null, parameterizedTypeReference, userId);
        log.debug("通过RestTemplate调用用户服务，获取用户信息，地址:{},入参为：{},出参为：{}", url, userId, entity);
        if (entity != null) {
            HttpStatus statusCode = entity.getStatusCode();
            if (HttpStatus.OK == statusCode) {
                JsonResult<UserInfoVO> body = entity.getBody();
                UserInfoVO data = body.getData();
                UserInfoDto userInfoDto = new UserInfoDto();
                userInfoDto.setCarId(data.getCarId());
                return userInfoDto;
            }
        }
        return null;
    }

    /**
     * 通过随机算法获取服务地址
     *
     * @return 返回服务地址信息
     */
    private String chooseUrl() {
        String url = "http://localhost:8080/user/info/{1}";
        String url1 = "http://localhost:8081/user/info/{1}";
        String url2 = "http://localhost:8082/user/info/{1}";
        String[] services = new String[]{url, url1, url2};
        Random random = new Random();

        int r = random.nextInt(services.length);
        return services[r];
    }

    /**
     * 获取订单号 唯一 需要保证分布式环境下的唯一性
     * 唯一的 随机的么？
     * 能不能用订单表的主键？主键自增，在分库分表时 不能保证唯一
     * 随机UUID 又不重复
     * UUID不能当主键  mysql数据库主键的索引底层实现有关系todo
     *
     * @return 订单号
     * <p>
     * 1.雪花算法  分布式id生成器 分布式场景下生成唯一id
     * 自己生成订单号
     * 2.固定开始1000+随机数+时间戳
     */
    private String getOrderId() {
        String start = "1000";
        String time = System.currentTimeMillis() + "";
        Random random = new Random();
        int i = random.nextInt(100);
        String orderNo = start + "_" + i + "_" + time;
        return orderNo;
    }


    @Autowired
    private DeviceClient deviceClient;

    /**
     * 获取场站信息和运营商信息
     *
     * @param gunId 枪编号
     * @return 场站信息和运营商信息
     */
    private StationInfoDto getStationInfoByGunId(Integer gunId) {
        JsonResult<StationInfoVO> stationInfo = deviceClient.getStationInfo(gunId);
        if (stationInfo != null) {
            StationInfoVO data = stationInfo.getData();
            if (data != null) {
                StationInfoDto stationInfoDto = new StationInfoDto();
                stationInfoDto.setStationId(data.getStationId());
                stationInfoDto.setOperatorId(data.getOperatorId());
                return stationInfoDto;
            }
        }
        return null;
    }
}
